#include "server.h"

int recvTrain(int netfd,int* tag, char* pathName){
    train_t train;
    bzero(&train,sizeof(train));
    int ret = recv(netfd, &train,sizeof(train_t),0);
    if(ret == 0){
        return -1;
    }
    *tag = train.tag;
    memcpy(pathName,train.data,train.length);
    return 0;
}

int recvn(int sockfd, void *buf, int length){
    int total = 0;
    char *p = (char *)buf;
    while(total < length){
        ssize_t sret = recv(sockfd,p+total,length-total,0);
        if(sret == 0){
            return -1;
        }
        total += sret;
    }
    return total;
}

int sendPWD(int netfd, char* pwd){
    train_t train;
    train.tag = PWD;
    train.length = strlen(pwd) + 1;
    memcpy(train.data,pwd,train.length);
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
    return 0;
}

int sendLS(int netfd,char** lsconbin){
    int i = 0;
    train_t train;
    while(lsconbin[i] != NULL){
        train.tag = LS;
        train.length = strlen(lsconbin[i]) + 1;
        memcpy(train.data,lsconbin[i],train.length);
        send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        ++i;
    }
    train.tag = FILEEND;
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
    return 0;
}

int getfile_c_to_s(int netfd,char* pathName,stackDir_t* dirStack,MYSQL* mysql,char* usrname){
    char vrpath[512] = {0};
    getvrpath(pathName,vrpath,dirStack,mysql);
    char filename[128] = {0};
    int parent_id;
    int ret = info_file(filename,&parent_id,vrpath,usrname,mysql);
    if(ret == -1){
        return -1;
    }

    train_t train;

    recvn(netfd,&train,sizeof(train));
    if(train.tag != FILENAME){
        printf("recv filename error in getFile.\n");
        return -1;
    }
    char fileName[1024] = {0};
    memcpy(fileName, train.data,train.length);

    off_t fileSize;
    recvn(netfd,&train,sizeof(train));
    memcpy(&fileSize,train.data,train.length);

    //hash
    char hash[65] = {0};
    recvn(netfd,&train,sizeof(train));
    memcpy(&hash,train.data,train.length);

    char realpath[1024] = {0};
    strcat(realpath,"storeFile/");
    strcat(realpath,hash);
    int flag = 0;
    if(access(realpath,F_OK) == 0){
        train.tag = 0;
        flag = 1;
    }
    train.tag = 1;
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);

    if(flag == 1){
        return 0;
    }

    char nickpathName[4096] = {0};
    strcat(nickpathName,realpath);
    strcat(nickpathName,".filepart");
    //offSet
    off_t offSet;
    int fd = open(nickpathName, O_RDWR | O_CREAT | O_APPEND, 0666);
    if(fd == -1){
        perror("open in getFile");
        return -1;
    }
    struct stat statbuf;
    fstat(fd,&statbuf);
    offSet = statbuf.st_size;

    train.tag = FILESIZE;
    train.length = sizeof(offSet);
    memcpy(train.data,&offSet,train.length);
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
    lseek(fd,offSet,SEEK_SET);
    printf("offSet = %ld\n",offSet);

    off_t curSize = offSet;
    off_t lastSize = offSet;
    off_t slice = fileSize / 1000;
    while(1){
        recvn(netfd,&train,sizeof(train));
        if(train.tag == FILEEND){
            break;
        }
        curSize += train.length;
        if(curSize - lastSize > slice){
            printf("%5.2lf%%\r", 100.0*curSize/fileSize);
            fflush(stdout); //手动刷新缓冲区
            lastSize = curSize;
        }
        write(fd,train.data,train.length);
    }
    rename(nickpathName,realpath);
    printf("100.00%%\n");
    printf("%s, filesize = %ld, transfer completed.\n",fileName,fileSize);
    close(fd);
    char hashfile[65] = {0};
    getHash(realpath,hashfile);
    if(strcmp(hash,hashfile) == 0){
        char sql[1024] = {0};
        sprintf(sql,"insert into file (file_name,user_name,parent_id,type,path,hash,size) value ('%s','%s',%d,'f','%s','%s',%ld);",fileName,usrname,parent_id,vrpath,hash,fileSize);
        int qret = mysql_query(mysql,sql);
        if(qret != 0){
            fprintf(stderr,"%s:%s\n","mysql_query",mysql_error(mysql));
        }
        train.tag = 0;
        send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        return 0;
    }else{
        unlink(realpath);
        train.tag = 1;
        send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        return 1;
    }
}

int putfile_s_to_c(int netfd,char* pathname,stackDir_t* dirStack,MYSQL* mysql,char* usrname){
    char vrpath[512] = {0};
    getvrpath(pathname,vrpath,dirStack,mysql);

    train_t train;

    char sql[1024] = {0};
    sprintf(sql,"select file_name,hash,size from file where path = '%s' AND user_name = '%s';",vrpath,usrname);
    int qret = mysql_query(mysql,sql);
    if(qret != 0){
        fprintf(stderr,"%s:%s\n","mysql_query",mysql_error(mysql));
    }
    MYSQL_RES* result = mysql_store_result(mysql);
    if(result == NULL){
        return -1;
    }
    MYSQL_ROW row = mysql_fetch_row(result);

    train.tag = FILENAME;
    train.length = strlen(row[0]) + 1;
    memcpy(train.data,row[0],train.length);
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
    char realpath[1024] = {0};
    strcat(realpath,"storeFile");
    strcat(realpath,"/");
    strcat(realpath,row[1]);
    int fd = open(realpath, O_RDWR);
    if(fd == -1){
        perror("open in putFile.");
        return -1;
    }

    off_t fileSize = atoi(row[2]);
    train.tag = FILESIZE;
    train.length = sizeof(fileSize);
    memcpy(&train.data,&fileSize,train.length);
    send(netfd,&train,sizeof(train),MSG_NOSIGNAL);

    off_t offSet;
    recvn(netfd,&train,sizeof(train));
    memcpy(&offSet,train.data,train.length);
    printf("offSet = %ld\n",offSet);

    if(fileSize < THRESHOLD){
        lseek(fd,offSet,SEEK_SET);
        while(1){
            bzero(&train,sizeof(train));
            train.tag = FILEDATA;
            int ret = read(fd,train.data,sizeof(train.data));
            if(ret == 0){
                break;
            }
            train.length = ret;
            send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        }
        train.tag = FILEEND;
        train.length = 0;
        send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        printf("send %s, fileszie = %ld.\n",row[0],fileSize);
    }else{
        char* p = (char *)mmap(NULL,fileSize,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0);
        train.tag = FILEDATA;
        while(offSet < fileSize){
            if(fileSize - offSet > 1000){
                train.length = 1000;
            }else{
                train.length = fileSize - offSet;
            }
            memcpy(train.data,p+offSet,train.length);
            send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
            offSet += train.length;
        }
        train.tag = FILEEND;
        train.length = 0;
        send(netfd,&train,sizeof(train),MSG_NOSIGNAL);
        printf("send %s, fileszie = %ld.\n",row[0],fileSize);
        munmap(p,fileSize);
    }
    close(fd);
    return 0;
}

int removefile(char* pathname,stackDir_t* dirStack,char* usrname,MYSQL* mysql){
    char vrpath[512] = {0};
    getvrpath(pathname,vrpath,dirStack,mysql);
    char sql[1024];
    sprintf(sql,"update file set is_delete = 1 where path = '%s' AND user_name = '%s';",vrpath,usrname);
    int qret = mysql_query(mysql,sql);
    if(qret != 0){
        fprintf(stderr,"%s:%s\n","mysql_query",mysql_error(mysql));
    }
    return 0;
}

int getHash(char* filename,char* hash){
    int fd  = open(filename, O_RDWR);
    if (fd == -1) {
        perror("Failed to open file");
        return -1;
    }

    SHA256_CTX sha256;
    SHA256_Init(&sha256);

    unsigned char buffer[4096];

    while (1) {
        bzero(buffer,4096);
        ssize_t sret = read(fd,buffer,sizeof(buffer));
        if(sret == 0){
            break;
        }
        SHA256_Update(&sha256, buffer, sret);
    }

    unsigned char md[SHA256_DIGEST_LENGTH];
    SHA256_Final(md, &sha256);
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++) {
        sprintf(hash + (i * 2), "%02x", md[i]); // %02x 表示 2 位十六进制，不足补零
    }
    hash[64] = '\0';
    close(fd);
    return 0;
}
